﻿#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using AbstractClass.Collections.Generic;
using AbstractClass.Extensions;

namespace AbstractClass.Collections
{
    /// <summary>
    /// Represents a matrix whose data are <see cref="ComplexNumber"/>s.
    /// </summary>
    [Serializable]
    public sealed class ComplexMatrix : Matrix<ComplexNumber>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexMatrix"/> class.
        /// </summary>
        /// <param name="rowCount">The number of rows.</param>
        /// <param name="columnCount">The number of columns.</param>
        public ComplexMatrix(int rowCount, int columnCount) : base(rowCount, columnCount)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexMatrix"/> class.
        /// </summary>
        /// <param name="matrix">A <see cref="Matrix&lt;ComplexNumber&gt;"/> object whose data will be copied to the new <see cref="ComplexMatrix"/>.</param>
        public ComplexMatrix(Matrix<ComplexNumber> matrix) : base(matrix.RowCount, matrix.ColumnCount, matrix.GetData())
        {
        }

        private ComplexMatrix(int rowCount, int columnCount, ComplexNumber[] data) : base(rowCount, columnCount, data)
        {
        }

        /// <summary>
        /// Adds a <see cref="ComplexMatrix"/> with this instance.
        /// </summary>
        /// <param name="matrix">The matrix to add with this instance.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the addition of this instance and <paramref name="matrix"/>.</returns>
        public ComplexMatrix Add(ComplexMatrix matrix)
        {
            VerifySize(matrix);

            for (int i = 0; i < Data.Length; ++i)
                Data[i] += matrix.Data[i];

            return this;
        }

        /// <summary>
        /// Adds two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the addition of <paramref name="left"/> and <paramref name="right"/>.</returns>
        public static ComplexMatrix Add(ComplexMatrix left, ComplexMatrix right)
        {
            left.ThrowIfNull("left");
            ComplexMatrix m = Create(left);
            return m.Add(right);
        }

        /// <summary>
        /// Adds two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the addition of <paramref name="left"/> and <paramref name="right"/>.</returns>
        public static ComplexMatrix operator +(ComplexMatrix left, ComplexMatrix right)
        {
            return Add(left, right);
        }

        /// <summary>
        /// Subtracts a <see cref="ComplexMatrix"/> from this instance.
        /// </summary>
        /// <param name="matrix">The matrix to subtract from this instance.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the subtraction of <paramref name="matrix"/> from this instance.</returns>
        public ComplexMatrix Subtract(ComplexMatrix matrix)
        {
            VerifySize(matrix);

            for (int i = 0; i < Data.Length; ++i)
                Data[i] -= matrix.Data[i];

            return this;
        }

        /// <summary>
        /// Subtracts two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the subtraction of <paramref name="left"/> and <paramref name="right"/>.</returns>
        public static ComplexMatrix Subtract(ComplexMatrix left, ComplexMatrix right)
        {
            ComplexMatrix m = Create(left);
            return m.Subtract(right);
        }


        /// <summary>
        /// Subtracts two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the subtraction of <paramref name="left"/> and <paramref name="right"/>.</returns>
        public static ComplexMatrix operator -(ComplexMatrix left, ComplexMatrix right)
        {
            return Subtract(left, right);
        }

        /// <summary>
        /// Multiplies this instance with the specified factor.
        /// </summary>
        /// <param name="factor">The factor to multiply with this instance.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of this instance and <paramref name="factor"/>. </returns>
        public ComplexMatrix Product(ComplexNumber factor)
        {
            for (int i = 0; i < Data.Length; ++i)
                Data[i] *= factor;
            return this;
        }

        /// <summary>
        /// Multiplies a <see cref="ComplexMatrix"/> and a <see cref="ComplexNumber"/>.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="factor">The factor to multiply with <paramref name="left"/>.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="left"/> and <paramref name="factor"/>.</returns>
        public static ComplexMatrix Product(ComplexMatrix left, ComplexNumber factor)
        {
            ComplexMatrix m = Create(left);
            return m.Product(factor);
        }

        /// <summary>
        /// Multiplies a <see cref="ComplexMatrix"/> and a <see cref="ComplexNumber"/>.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="factor">The factor to multiply with <paramref name="left"/>.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="left"/> and <paramref name="factor"/>.</returns>
        public static ComplexMatrix operator *(ComplexMatrix left, ComplexNumber factor)
        {
            return Product(left, factor);
        }

        /// <summary>
        /// Multiplies a <see cref="ComplexNumber"/> and a <see cref="ComplexMatrix"/>.
        /// </summary>
        /// <param name="factor">The factor to multiply with <paramref name="right"/>.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="right"/> and <paramref name="factor"/>.</returns>
        public static ComplexMatrix Product(ComplexNumber factor, ComplexMatrix right)
        {
            ComplexMatrix m = Create(right);
            return m.Product(factor);
        }

        /// <summary>
        /// Multiplies a <see cref="ComplexNumber"/> and a <see cref="ComplexMatrix"/>.
        /// </summary>
        /// <param name="factor">The factor to multiply with <paramref name="right"/>.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="right"/> and <paramref name="factor"/>.</returns>
        public static ComplexMatrix operator *(ComplexNumber factor, ComplexMatrix right)
        {
            return Product(factor, right);
        }

        /// <summary>
        /// Multiplies two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
        /// <exception cref="MatrixSizeMismatchException">Number of columns in <paramref name="left"/> is not equal to number of rows in <paramref name="right"/>.</exception>
        public static ComplexMatrix Product(ComplexMatrix left, ComplexMatrix right)
        {
            left.ThrowIfNull("left");
            right.ThrowIfNull("right");
            if (left.ColumnCount != right.RowCount)
                throw new MatrixSizeMismatchException(left.RowCount, left.ColumnCount, right.RowCount, right.ColumnCount);

            var m = new ComplexMatrix(left.RowCount, right.ColumnCount);
            for (int i = 0; i < left.RowCount; ++i)
            {
                for (int j = 0; j < left.ColumnCount; ++j)
                {
                    for (int k = 0; k < right.ColumnCount; ++k)
                    {
                        m[i, k] += left[i, j] * right[j, k];
                    }
                }
            }

            return m;
        }

        /// <summary>
        /// Multiplies two <see cref="ComplexMatrix"/> instances.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
        /// <exception cref="MatrixSizeMismatchException">Number of columns in <paramref name="left"/> is not equal to number of rows in <paramref name="right"/>.</exception>
        public static ComplexMatrix operator *(ComplexMatrix left, ComplexMatrix right)
        {
            return Product(left, right);
        }

        /// <summary>
        /// Divides the current <see cref="ComplexMatrix"/> by a <see cref="ComplexNumber"/>.
        /// </summary>
        /// <param name="factor">The factor to divide this instance.</param>
        /// <returns>A new <see cref="ComplexMatrix"/> which is equal to the division of this instance by <paramref name="factor"/>.</returns>
        public ComplexMatrix Divide(ComplexNumber factor)
        {
            for (int i = 0; i < Data.Length; ++i)
                Data[i] = checked(Data[i] / factor);

            return this;
        }

        /// <summary>
        /// Calculates the Induced norm (p-norms) of this instance while p = 1.
        /// </summary>
        /// <returns>
        /// A <see cref="ComplexNumber"/> which is equal to the 1-norm of this instance.
        /// </returns>
        public ComplexNumber GetNorm1()
        {
            ComplexNumber norm = ComplexNumber.Zero;
            for (int i = 0; i < RowCount; ++i)
            {
                ComplexNumber sum = ComplexNumber.Zero;
                for (int j = 0; j < ColumnCount; ++j)
                {
                    sum += this[i, j];
                }

                norm = QMath.Max(sum, norm);
            }

            return norm;
        }

        /// <summary>
        /// Calculates the Induced norm (p-norms) of this instance while p = ∞.
        /// </summary>
        /// <returns>
        /// A <see cref="ComplexNumber"/> which is equal to the infinity-norm of this instance.
        /// </returns>
        public ComplexNumber GetNormInfinity()
        {
            ComplexNumber norm = ComplexNumber.Zero;
            for (int j = 0; j < ColumnCount; ++j)
            {
                ComplexNumber sum = ComplexNumber.Zero;
                for (int i = 0; i < RowCount; ++i)
                {
                    sum += this[i, j];
                }
                norm = QMath.Max(sum, norm);
            }

            return norm;
        }

        /// <summary>
        /// Calculates the Euclidean norm of this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="ComplexNumber"/> which is equal to the Euclidean norm of this instance.
        /// </returns>
        public ComplexNumber GetNorm2()
        {
            ComplexNumber norm = ComplexNumber.Zero;
            for (int i = 0; i < Count; ++i)
            {
                norm += Data[i] * Data[i];
            }
            return QMath.Sqrt(norm);
        }

        /// <summary>
        /// Gets the identity <see cref="ComplexMatrix"/>.
        /// </summary>
        /// <param name="count">Number of rows / columns in the identity matrix.</param>
        /// <returns>A <see cref="ComplexMatrix"/> which is an identity matrix.</returns>
        public static ComplexMatrix Identity(int count)
        {
            var m = new ComplexMatrix(count, count);
            for (int i = 0; i < count; ++i)
                m[i, i] = ComplexNumber.One;
            return m;
        }

        /// <summary>
        /// Creates a new <see cref="ComplexMatrix"/> from a specified matrix.
        /// </summary>
        /// <param name="matrix">The matrix.</param>
        /// <returns></returns>
        public new static ComplexMatrix Create(Matrix<ComplexNumber> matrix)
        {
            var data = new ComplexNumber[matrix.GetData().LongLength];
            matrix.GetData().CopyTo(data, 0);
            return new ComplexMatrix(matrix.RowCount, matrix.ColumnCount, data);
        }
    }
}